/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.utils;

import java.beans.*;
import java.lang.reflect.*;
import java.util.*;
import org.junit.*;
import ch.trackedbean.common.*;
import ch.trackedbean.util.*;

/**
 * Utils class for Tests.
 * 
 * @author hautle
 */
public class BeanTestUtil {
	/**
	 * Sorts the givne list by the id's.
	 * 
	 * @param <T> The bean type
	 * 
	 * @param list The list to sort
	 * @param type The content type of the list
	 * @param attr The name of the attribute used to sort the list
	 * @return The sorted list
	 */
	public static <T> List<T> sort(List<T> list, Class<T> type, String attr) {
		Collections.sort(list, new ElementValueComparator<T>(type, attr));
		return list;
	}

	/**
	 * Checks the Beans in the given list for theyr equality.
	 * 
	 * @param message The message for the assertion
	 * @param expected The list holding the expected values
	 * @param actual The list to check
	 * @param attrs The list of the properties to check (for each list entry)
	 */
	public static void checkContentProperties(String message, List<?> expected, List<?> actual, String... attrs) throws IllegalArgumentException,
			TechnicalException, IllegalAccessException, InvocationTargetException {
		Assert.assertTrue(message + ": Not same element count", expected.size() == actual.size());
		for (int i = 0, cnt = expected.size(); i < cnt; i++)
			checkProperties(message, expected.get(i), actual.get(i), attrs);
	}

	/**
	 * Checks the Beans in the given list for theyr equality.
	 * 
	 * @param message The message for the assertion
	 * @param expected The list holding the expected values
	 * @param actual The list to check
	 * @param ignore The list of the properties to ignore
	 * @throws Exception If something goes wrong
	 */
	public static void checkBeans(String message, List<?> expected, List<?> actual, String... ignore) throws Exception {
		Assert.assertTrue(message + ": Not same element count", expected.size() == actual.size());
		for (int i = 0, cnt = expected.size(); i < cnt; i++)
			checkBeans(message, expected.get(i), actual.get(i), ignore);
	}

	/**
	 * Checks two beans on their equalty.
	 * 
	 * @param <T> The bean type
	 * 
	 * @param message The message for the assertion
	 * @param expected The bean holding the expected values
	 * @param actual The bean to check
	 * @param ignore The list of the properties to ignore
	 */
	public static <T> void checkBeans(String message, T expected, T actual, String... ignore) throws Exception {
		Arrays.sort(ignore);
		for (PropertyDescriptor d : Introspector.getBeanInfo(expected.getClass()).getPropertyDescriptors()) {
			// skip ignore properties
			if (Arrays.binarySearch(ignore, d.getName()) > -1)
				continue;
			final Method read = d.getReadMethod();
			final Object valA = read.invoke(expected);
			final Object valB = read.invoke(actual);
			if (valA instanceof Collection || valA instanceof Object[]) {
				final List listA = asList(valA);
				final List listB = asList(valB);
				checkBeans(message, listA, listB);
				continue;
			}
			Assert.assertEquals(message + ": " + d.getName(), valA, valB);
		}
	}

	/**
	 * Checks a serie of properties on theyr equality.
	 * 
	 * @param message The message for the assertion
	 * @param expected The bean holding the expected values
	 * @param actual The bean to check
	 * @param attrs The list of the properties to check
	 */
	public static void checkProperties(String message, Object expected, Object actual, String... attrs) throws IllegalArgumentException, TechnicalException,
			IllegalAccessException, InvocationTargetException {
		final Class<? extends Object> ca = expected.getClass();
		final Class<? extends Object> cb = actual.getClass();
		for (String attr : attrs) {
			final Object valA = BeanUtils.getDescriptor(ca, attr).getReadMethod().invoke(expected);
			final Object valB = BeanUtils.getDescriptor(cb, attr).getReadMethod().invoke(actual);
			Assert.assertEquals(message + ": " + attr, valA, valB);
		}
	}

	/**
	 * Checks a serie of properties on theyr difference.
	 * 
	 * @param message The message for the assertion
	 * @param expected The bean holding the expected values
	 * @param actual The bean to check
	 * @param attrs The list of the properties to check
	 */
	public static void checkDifferentProperties(String message, Object expected, Object actual, String... attrs) throws IllegalArgumentException,
			TechnicalException, IllegalAccessException, InvocationTargetException {
		final Class<? extends Object> ca = expected.getClass();
		final Class<? extends Object> cb = actual.getClass();
		for (String attr : attrs) {
			final Object valA = BeanUtils.getDescriptor(ca, attr).getReadMethod().invoke(expected);
			final Object valB = BeanUtils.getDescriptor(cb, attr).getReadMethod().invoke(actual);
			Assert.assertTrue(message + ": " + attr, valA == null && valB != null || valB == null && valA != null || valA != null && !valA.equals(valB));
		}
	}

	/**
	 * 'Converts' the given source to a collection.
	 * 
	 * @param src The source or null
	 * @return The source as collection or null
	 */
	public static List asList(Object src) {
		if (src == null)
			return null;
		if (src instanceof List)
			return (List) src;
		if (src instanceof Collection)
			return new ArrayList((Collection) src);
		return Arrays.asList((Object[]) src);
	}
}
